﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq.Mapping;
using System.Collections.ObjectModel;

namespace Storage.EntityModels
{
    using Exceptions;

    #region Validator

    /// <summary>
    /// Perform validation logic for the NVarChar fields.
    /// </summary>
    static class Validator
    {
        /// <summary>
        /// The position at which the declaration of the lenght of NVarChar field starts.
        /// </summary>
        private const int FIELD_MAX_LENGTH_POS = 9;

        /// <summary>
        /// Check if all NVarChar fields of the object are valid.
        /// </summary>
        /// <param name="o">The object to validate.</param>
        public static void Validate(object o)
        {
            var constrainedFields = 

                // Get all properties in object o.
                from field in o.GetType().GetProperties()
                
                // Get all properties that have Column custom attribute.
                from columnAttributedFields in field.GetCustomAttributes(typeof(ColumnAttribute), false)

                // Get only the fields that have NVarChar as type, and the length is shorter than MAX.
                let dbType = ((ColumnAttribute)columnAttributedFields).DbType

                // NB: "ToUpper" is needed for dummy programmers that write "Max" instead of conventional "MAX"
                where dbType.Contains("NVarChar") && !dbType.ToUpper().Contains("MAX")
                select new FieldInfo
                {
                    Name = field.Name,
                    MaxLength = int.Parse(dbType.Substring(FIELD_MAX_LENGTH_POS, dbType.IndexOf(')') - FIELD_MAX_LENGTH_POS)),
                    Length = field.GetValue(o, null) != null ? ((string)field.GetValue(o, null)).Length : 0
                };

            var invalidFields = constrainedFields
                .Where(f => f.Length > f.MaxLength)
                .Select(f => f);
                
            if (invalidFields.Any())
            {
                // Some field is not valid.
                throw new FieldsNotValidException(invalidFields);
            }
        }

        /// <summary>
        /// Check if all NVarChar fields of the objects are valid.
        /// </summary>
        /// <param name="objects"></param>
        public static void Validate(IEnumerable<object> objects)
        {
            objects.ForEach(o => Validate(o));
        }
    }

    #endregion
}
